package member

import (
	"adai.design/jarvis/common/db"
	"adai.design/jarvis/common/log"
	"errors"
	"github.com/globalsign/mgo/bson"
	"strings"
	"time"
)

// 账号管理
type AccountManager struct{}

// 插入账号
func (am *AccountManager) Insert(account, password string) (*Account, error) {
	if a, _ := am.FindByAccount(account); a != nil {
		log.Info("%s", account)
		log.Info("%s", a)
		return nil, errors.New("account-already-exists")
	}

	session, err := db.GetSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()

	var a Account
	if strings.Contains(account, "@") {
		a.Email = account
	} else {
		a.Phone = account
	}
	a.Pwd = password
	a.Name = "默认用户名"
	a.Icon = "jarvis"
	a.Id = db.NewId()
	a.RegDate = time.Now()
	a.Homes = []*Home{
		{Id: db.NewId(), Role: RoleMaster},
	}

	collection := session.DB("member").C("account")
	err = collection.Insert(&a)
	if err != nil {
		return nil, err
	}
	return &a, nil
}

func (am *AccountManager) Remove(uid string) error {
	session, err := db.GetSession()
	if err != nil {
		return err
	}
	defer session.Close()

	collection := session.DB("member").C("account")
	err = collection.Remove(bson.M{"uid": uid})
	return err
}

// 保存账号
func (am *AccountManager) Update(a *Account) error {
	session, err := db.GetSession()
	if err != nil {
		return err
	}
	defer session.Close()

	collection := session.DB("member").C("account")
	err = collection.Update(bson.M{"uid": a.Id}, a)
	return err
}

// 查找所有账号
func (am *AccountManager) findAll() ([]*Account, error) {
	session, err := db.GetSession()
	if err != nil {
		return nil, err
	}

	collection := session.DB("member").C("account")
	var as []*Account
	err = collection.Find(bson.M{}).All(&as)
	if err != nil {
		return nil, err
	}
	return as, nil
}

// 查找账户
func (am *AccountManager) FindByAccount(account string) (*Account, error) {
	if account == "" {
		return nil, errors.New("invalid parameter")
	}

	var query bson.M
	if strings.Contains(account, "@") {
		query = bson.M{"email": account}
	} else {
		query = bson.M{"phone": account}
	}

	session, err := db.GetSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()

	collection := session.DB("member").C("account")

	var a Account
	err = collection.Find(query).One(&a)
	if err != nil {
		return nil, err
	}
	return &a, nil
}

func (am *AccountManager) FindById(id string) (*Account, error) {
	session, err := db.GetSession()
	if err != nil {
		return nil, err
	}
	defer session.Close()

	collection := session.DB("member").C("account")
	var a Account
	err = collection.Find(bson.M{"uid": id}).One(&a)

	if err != nil {
		return nil, err
	}
	return &a, nil
}

// 客户端(账号密码登录)
func (am *AccountManager) Login(account, password, devType string) (*Account, *Client, error) {
	a, err := am.FindByAccount(account)
	if err != nil || a == nil {
		return nil, nil, errors.New("account-not-exists")
	}

	if a.Pwd != password {
		return nil, nil, errors.New("password-error")
	}

	clients := a.findClients()
	if clients == nil {
		clients = &Clients{
			UserId:  a.Id,
			Clients: []*Client{},
		}
	} else {
		for i, v := range clients.Clients {
			if v.DevType == devType {
				// 删除旧的客户端
				clients.Clients = append(clients.Clients[:i], clients.Clients[i+1:]...)
			}
		}
	}

	client := &Client{
		DevType: devType,
		Session: a.sessionKey(devType),
		Last:    time.Now(),
	}
	clients.Clients = append(clients.Clients, client)
	clients.Save()
	log.Trace("%s", clients)
	return a, client, nil
}

// Token验证与登录
func (am *AccountManager) SessionCheck(uid, token, devType string) (*Account, *Client, error) {
	a, err := am.FindById(uid)
	if err != nil || a == nil {
		return nil, nil, errors.New("account-not-exists")
	}
	clients := a.findClients()
	if clients == nil || len(clients.Clients) == 0 {
		return nil, nil, errors.New("token-error")
	}

	var c *Client
	for _, v := range clients.Clients {
		if v.DevType == devType && token == v.Session {
			c = v
		}
	}
	if c == nil {
		return nil, nil, errors.New("token-error")
	}
	c.Last = time.Now()
	clients.Save()
	return a, c, nil
}

var accounts = AccountManager{}

func FindAccountById(id string) (*Account, error) {
	return accounts.FindById(id)
}
